/*****************************************************************************
 * Debug utility for crossbit
 *
 * Author: Zheng Juyu
 ****************************************************************************/

#ifndef _IA32DEBUGGER_H_
#define _IA32DEBUGGER_H_

#include "crossbit/VBlock.h"
#include "crossbit/ECOFFMemoryImage.h"
#include "crossbit/ELFMemoryImage.h"
#include "crossbit/IA32Interpreter.h"
#include "crossbit/IA32Inst.h"
#include "crossbit/IA32BlockContextManager.h"
#include "crossbit/debugger.h"

namespace crossbit
{
class TCache;
class IA32Interpreter;
class IA32Syscall;
class X86Translator;
class IA32BasicBlockBuilder;

    void ia32_debugger_init( ELFMemoryImage* img, 
			TCache* cache, 
			ProcessorState* cpu,
			IA32Interpreter* interp,
			IA32Syscall* syscall,
			X86Translator* translator,
			IA32BasicBlockBuilder* builder
			);

    struct IA32BreakPoint : BreakPoint
    {
	IA32OpsFmt inst;
    };
    
    int disas( IA32OpsFmt* pInst, char* buf, MemAddr CPC);

    class IA322x86Debugger
    {
	public:
	     IA322x86Debugger(): pState_(NULL), pImage_(NULL), pInterp_(NULL),
				 pSyscall_(NULL), pTranslator_(NULL), 
				 pBuilder_(NULL),
				 pCache_(NULL), curPC_(0), pCurInst_(NULL) 
	     {
		 //empty
	     }

	     static IA322x86Debugger* getInstance()
	     {
		 if( pInstance_ == NULL )
		 {
		     pInstance_ = new IA322x86Debugger();
		 }

		 return pInstance_;
	     }

	     void usage()
	     {
		 std::cout << "Debugger Usage: " << std::endl
		     	<<"b(break) address -- Set a breakpoint at specified address"<<std::endl
			<<"clear address -- Clear the breakpoint at specified address" << std::endl
			<<"s(step) -- Single step execution" << std::endl
			<<"c(cont) -- Continue program being debugged" << std::endl
			<<"r(regs) -- Display register values" << std::endl
			<<"iregs -- Display integer register values" << std::endl
			<<"fregs -- Display float point register values" << std::endl
			<<"x /fmt address -- Display memory at specified address" << std::endl
			<<"back -- Reverse execute one instruction" << std::endl
			<<"q -- Exit the debugger" << std::endl;
	     }

	     void init( ProcessorState* state, 
		     MemoryImage* img,
		     IA32Interpreter* interp,
		     IA32Syscall* syscall,
		     X86Translator* translator,
		     IA32BasicBlockBuilder* builder,
		     TCache* cache)
	     {
		 this->pState_ = state;
		 this->pImage_ = img;
		 this->pInterp_ = interp;
		 this->pSyscall_ = syscall;
		 this->pTranslator_ = translator;
		 this->pBuilder_ = builder;
		 this->pCache_ = cache;
	     }

	     void process_exception( int signum, char* addr );
	     
	     void console();
	     
	     void saveBlockContext( int enterPC, int lastPC );
	     
	     //reverse execute one instruction
	     void do_backTracking();
	     
	     //execute the next instruction
	     void step();
	     
	     //continue executing
	     void go();
	     
	     void add_breakpoint(MemAddr addr);
	     void clear_breakpoint(MemAddr addr);
	     void display_memory(MemAddr addr, char* fmt, int num );

	private:
	     void prompt()
	     {
		 std::cout << std::endl;
		 std::cout << "(cdb) ";
	     }

	     void display_current_instruction()
	     {
		 char buf[128];

		 if( disas(pCurInst_, buf, curPC_) )
		 {
		     printf("0x%08x:\t%s\n", curPC_, buf);
		 }
		 else
		 {
		     printf("Unknown instruction\n");
		 }
	     }

	     void display_regs()
	     {
		 std::cout << "Integer registers value:" << std::endl;
		 display_iregs();
		 std::cout << "Floating point registers values:" << std::endl;
		 display_fregs();
	     }

	     void display_iregs()
	     {
		 pState_->dump();
		 /*int size = pState_->size();
		 
		 for( int i = 0; i < size; i ++ )
		 {
		     std::cout << "$r" << i << ": 0x" 
			 << std::hex << pState_->reg(i) << "\t";

		     if( i % 2 == 1 )
			 std::cout << std::endl;
		 }*/
	     }

	     void display_fregs()
	     {
		 int size = pState_->fpsize();

		 for( int i = 0; i < size; i ++ )
		 {
		     int *p = (int*)pState_->fpget(i);
		     printf("$f%d: %08x%08x%08x\t", i, *p, *(p+1), *(p+2)); 
		     //std::cout << "$f" << i << ": 0x"
		//	 << std::hex << pState_->fpreg(i) << "\t";

		     if( i % 2 == 1 )
			 std::cout << std::endl;
		 }
	     }
	     
	private:
	    ProcessorState* pState_;
	    MemoryImage* pImage_;
	    
	    IA32Interpreter* pInterp_;
	    IA32Syscall* pSyscall_;
	    X86Translator* pTranslator_;
	    IA32BasicBlockBuilder* pBuilder_;
	    TCache* pCache_;
	    
	    MemAddr curPC_;
	    IA32OpsFmt* pCurInst_;

	    IA32BlockContextManager bcManager_;
	    
	    static IA322x86Debugger* pInstance_;
	    
	private:
	    //Set of breakpoints, index by real address
	    std::map< char*, BreakPoint* > rmap_;

	    //Set of breakpoints, index by virtual address
	    std::map< MemAddr, BreakPoint* > vmap_;
    };
}

#endif //_IA32DEBUGGER_H_
